package ui.component;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import model.Attribute;
import model.Database;
import model.Table;
import mongodb.MongoDb;
import ui.DatabaseHandler;

/**
 *
 * @author Merli András-Bertalan
 */
public class ContentView extends VBox {

    private Table currentTable;
    private Database currentDatabase;
    private List<Table> indexTables;
    private TableView<List<String>> tableView;
    private ObservableList<List<String>> data;
    private Button addNewItemButton;
    private Button deleteSelectedItemButton;

    public final void updateUi(final Database currentDatabase, final Table currentTable, final List<Table> indexTables, final DatabaseHandler databaseHandler) {
        this.currentTable = currentTable;
        this.indexTables = indexTables;
        this.currentDatabase = currentDatabase;

        getChildren().clear();

        // Table attribute table
        tableView = new TableView();
        tableView.setEditable(
                true);
        final Label label = new Label("Edit Table Values");
        label.setFont(
                new Font("Arial", 20));

        final List<List<String>> values = currentTable.getValues();
        data = FXCollections.observableArrayList();
        data.addAll(values);

        final List<Attribute> attributes = currentTable.getAttributes();
        for (int i = 0; i < attributes.size(); i++) {
            final int colNr = i;
            TableColumn tc = new TableColumn(attributes.get(colNr).getName());
            tc.setCellFactory(TextFieldTableCell.forTableColumn());
            tc.setPrefWidth(90);
            tc.setCellValueFactory(new Callback<CellDataFeatures<List<String>, String>, ObservableValue<String>>() {
                @Override
                public ObservableValue<String> call(CellDataFeatures<List<String>, String> p) {
                    return new SimpleStringProperty(p.getValue().get(colNr));
                }
            });
            tc.setOnEditCommit(
                    new EventHandler<TableColumn.CellEditEvent<List<String>, String>>() {
                        @Override
                        public void handle(final TableColumn.CellEditEvent<List<String>, String> t) {
                            final String newValue = t.getNewValue();
                            final String oldValue = t.getOldValue();
                            final List<String> item = t.getRowValue();
                            final int colNr = t.getTablePosition().getColumn();

                            String primaryValue = null;
                            for (int i = 0; i < attributes.size(); i++) {
                                if (attributes.get(i).getIsPrimary()) {
                                    primaryValue = item.get(i);
                                }
                            }

                            if (attributes.get(colNr).getReferenceTable() != null) {
                                try {
                                    if (!MongoDb.isKeyInTable(currentDatabase, attributes.get(colNr).getReferenceTable(), newValue)) {
                                        Stage dialog = new Stage();
                                        dialog.initStyle(StageStyle.UTILITY);
                                        Scene scene = new Scene(new Group(new Text(25, 25, "This value is not available!")));
                                        dialog.setScene(scene);
                                        dialog.setMinHeight(100);
                                        dialog.setMinWidth(200);
                                        dialog.show();
                                        dialog.setOnCloseRequest(new EventHandler<WindowEvent>() {
                                            @Override
                                            public void handle(WindowEvent t) {
                                                item.set(colNr, oldValue);
                                                tableView.getColumns().get(0).setVisible(false);
                                                tableView.getColumns().get(0).setVisible(true);
                                            }
                                        });
                                        return;
                                    }
                                } catch (UnknownHostException ex) {
                                    Logger.getLogger(ContentView.class.getName()).log(Level.SEVERE, null, ex);
                                }
                            }

                            if (attributes.get(colNr).getIsUnique()) {
                                int itemNr = data.indexOf(item);
                                boolean isUnique = true;
                                for (int i = 0; i < data.size(); i++) {
                                    if (itemNr != i && data.get(i).get(colNr).equals(newValue)) {
                                        isUnique = false;
                                    }
                                }

                                if (isUnique) {
                                    item.set(colNr, newValue);

                                    // update index tables
                                    if (attributes.get(colNr).getIsPrimary()) {
                                        // primary
                                        for (Table indexTable : indexTables) {
                                            if (indexTable.getName().contains(currentTable.getName() + "_")) {
                                                for (List<String> indexItem : indexTable.getValues()) {
                                                    String value = indexItem.get(1);
                                                    String nv = "";
                                                    for (String ov : value.split(" ")) {
                                                        if (ov.equals(oldValue)) {
                                                            if (nv.equals("")) {
                                                                nv = newValue;
                                                            } else {
                                                                nv = nv.concat(" ").concat(newValue);
                                                            }
                                                        } else {
                                                            if (nv.equals("")) {
                                                                nv = ov;
                                                            } else {
                                                                nv = nv.concat(" ").concat(ov);
                                                            }
                                                        }
                                                    }
                                                    indexItem.set(1, nv);
                                                }
                                            }
                                        }
                                    } else if (attributes.get(colNr).getIsIndexed()) {
                                        // not primary, but indexed
                                        for (Table indexTable : indexTables) {
                                            if (indexTable.getName().contains(currentTable.getName() + "_" + attributes.get(colNr).getName())) {
                                                List<String> newKeyItem = null;
                                                List<String> oldKeyItem = null;
                                                for (int i = 0; i < indexTable.getValues().size(); i++) {
                                                    List<String> indexItem = indexTable.getValues().get(i);
                                                    if (indexItem.get(0).equals(newValue)) {
                                                        newKeyItem = indexItem;
                                                    } else if (indexItem.get(0).equals(oldValue)) {
                                                        oldKeyItem = indexItem;
                                                    }
                                                }

                                                if (newKeyItem == null) {
                                                    if (!oldKeyItem.get(1).contains(" ")) {
                                                        oldKeyItem.set(0, newValue);
                                                    } else {
                                                        oldKeyItem.set(1, oldKeyItem.get(1).replace(primaryValue + " ", ""));
                                                        oldKeyItem.set(1, oldKeyItem.get(1).replace(" " + primaryValue, ""));
                                                        indexTable.getValues().add(Arrays.asList(newValue, primaryValue));
                                                    }
                                                } else {
                                                    newKeyItem.set(1, newKeyItem.get(1).concat(" ").concat(primaryValue));
                                                    if (!oldKeyItem.get(1).contains(" ")) {
                                                        indexTable.getValues().remove(oldKeyItem);
                                                    } else {
                                                        oldKeyItem.set(1, oldKeyItem.get(1).replace(primaryValue + " ", ""));
                                                        oldKeyItem.set(1, oldKeyItem.get(1).replace(" " + primaryValue, ""));
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    Stage dialog = new Stage();
                                    dialog.initStyle(StageStyle.UTILITY);
                                    Scene scene = new Scene(new Group(new Text(25, 25, "This value is not unique!")));
                                    dialog.setScene(scene);
                                    dialog.setMinHeight(100);
                                    dialog.setMinWidth(200);
                                    dialog.show();
                                    dialog.setOnCloseRequest(new EventHandler<WindowEvent>() {
                                        @Override
                                        public void handle(WindowEvent t) {
                                            item.set(colNr, oldValue);
                                            tableView.getColumns().get(0).setVisible(false);
                                            tableView.getColumns().get(0).setVisible(true);
                                        }
                                    });
                                }
                            } else {
                                item.set(colNr, newValue);

                                // update index tables
                                if (attributes.get(colNr).getIsPrimary()) {
                                    // primary
                                    for (Table indexTable : indexTables) {
                                        if (indexTable.getName().contains(currentTable.getName() + "_")) {
                                            for (List<String> indexItem : indexTable.getValues()) {
                                                String value = indexItem.get(1);
                                                String nv = "";
                                                for (String ov : value.split(" ")) {
                                                    if (ov.equals(oldValue)) {
                                                        if (nv.equals("")) {
                                                            nv = newValue;
                                                        } else {
                                                            nv = nv.concat(" ").concat(newValue);
                                                        }
                                                    } else {
                                                        if (nv.equals("")) {
                                                            nv = ov;
                                                        } else {
                                                            nv = nv.concat(" ").concat(ov);
                                                        }
                                                    }
                                                }
                                                indexItem.set(1, nv);
                                            }
                                        }
                                    }
                                } else if (attributes.get(colNr).getIsIndexed()) {
                                    // not primary, but indexed
                                    for (Table indexTable : indexTables) {
                                        if (indexTable.getName().contains(currentTable.getName() + "_" + attributes.get(colNr).getName())) {
                                            List<String> newKeyItem = null;
                                            List<String> oldKeyItem = null;
                                            for (int i = 0; i < indexTable.getValues().size(); i++) {
                                                List<String> indexItem = indexTable.getValues().get(i);
                                                if (indexItem.get(0).equals(newValue)) {
                                                    newKeyItem = indexItem;
                                                } else if (indexItem.get(0).equals(oldValue)) {
                                                    oldKeyItem = indexItem;
                                                }
                                            }

                                            if (newKeyItem == null) {
                                                if (!oldKeyItem.get(1).contains(" ")) {
                                                    oldKeyItem.set(0, newValue);
                                                } else {
                                                    oldKeyItem.set(1, oldKeyItem.get(1).replace(primaryValue + " ", ""));
                                                    oldKeyItem.set(1, oldKeyItem.get(1).replace(" " + primaryValue, ""));
                                                    indexTable.getValues().add(Arrays.asList(newValue, primaryValue));
                                                }
                                            } else {
                                                newKeyItem.set(1, newKeyItem.get(1).concat(" ").concat(primaryValue));
                                                if (!oldKeyItem.get(1).contains(" ")) {
                                                    indexTable.getValues().remove(oldKeyItem);
                                                } else {
                                                    oldKeyItem.set(1, oldKeyItem.get(1).replace(primaryValue + " ", ""));
                                                    oldKeyItem.set(1, oldKeyItem.get(1).replace(" " + primaryValue, ""));
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    });

            tableView.getColumns().add(tc);
        }
        tableView.setItems(data);

        addNewItemButton = new Button("Add New Item");
        addNewItemButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                int colNr = currentTable.getAttributes().size();
                List<String> emptyRow = new ArrayList<>(colNr);
                for (int i = 0; i < colNr; i++) {
                    emptyRow.add("new");
                }
                data.add(emptyRow);

                // add "new"s to the index tables
                for (Table indexTable : indexTables) {
                    if (indexTable.getName().contains(currentTable.getName() + "_")) {
                        indexTable.getValues().add(Arrays.asList("new", "new"));
                    }
                }
            }
        });

        deleteSelectedItemButton = new Button("Delete Selected Item");
        deleteSelectedItemButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                List<String> selectedItem = tableView.getSelectionModel().getSelectedItem();
                if (selectedItem != null) {
                    Attribute prAttribute = null;
                    int prAttributeIndex = 0;
                    for (int i = 0; i < currentTable.getAttributes().size(); i++) {
                        Attribute a = currentTable.getAttributes().get(i);
                        if (a.getIsPrimary() && a.getReferenceAttributeName() != null) {
                            prAttribute = a;
                            prAttributeIndex = i;
                        }
                    }

                    if (prAttribute == null) {
                        data.remove(selectedItem);
                    } else {
                        String prAttributeValue = selectedItem.get(prAttributeIndex);
                        String indexTableName = prAttribute.getReferenceTableName() + "_" + prAttribute.getReferenceAttributeName().get();
                        Table indexTable = databaseHandler.getIndexTableByDatabaseAndTableName(currentDatabase, indexTableName);
                        if (indexTable == null) {
                            data.remove(selectedItem);
                        } else {
                            try {
                                if (MongoDb.isKeyInTable(currentDatabase, indexTable, prAttributeValue)) {
                                    Stage dialog = new Stage();
                                    dialog.initStyle(StageStyle.UTILITY);
                                    Scene scene = new Scene(new Group(new Text(25, 25, "This value is a reference in the \"" + prAttribute.getReferenceTableName() + "\" table")));
                                    dialog.setScene(scene);
                                    dialog.setMinHeight(100);
                                    dialog.setMinWidth(200);
                                    dialog.show();
                                } else {
                                    data.remove(selectedItem);
                                }
                            } catch (UnknownHostException ex) {
                                Logger.getLogger(ContentView.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }
                }
            }
        }
        );

        getChildren()
                .addAll(tableView, addNewItemButton, deleteSelectedItemButton);
    }

    public void saveCurrentState() {
        List<List<String>> values = new ArrayList<>(data.size());
        values.addAll(data);
        currentTable.setValues(values);
    }

    public void hideAddAndDeleteButton() {
        addNewItemButton.setVisible(false);
        deleteSelectedItemButton.setVisible(false);
        tableView.setEditable(false);
    }
}
